package rpc

import (
	"context"
	"fmt"
	"net/http"

	"github.com/filecoin-project/go-jsonrpc"
	"github.com/filecoin-project/go-jsonrpc/auth"
	"github.com/gbrlsnchs/jwt/v3"
	"github.com/urfave/cli"
	"golang.org/x/xerrors"

	logging "github.com/ipfs/go-log/v2"
)

var AuthNewCmd = &cli.Command{
	Name:  "authnew",
	Usage: "创建一个新token",

	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "perm, p",                                                             // 配置名称
			Value: "read",                                                                // 缺省配置值
			Usage: "permission to assign to the token, one of: read, write, sign, admin", // 配置描述
		},
		cli.StringFlag{
			Name:        "secret, s", // 配置名称
			Value:       "123456",    // 缺省配置值
			Usage:       "jwt 加密密码,请修改默认密码",
			Destination: &Cfg.APISecret,
		},
	},
	Action: func(cctx *cli.Context) error {

		perm := cctx.String("perm")
		idx := 0
		for i, p := range AllPermissions {
			if auth.Permission(perm) == p {
				idx = i + 1
			}
		}
		//	Cfg.APISecret = cctx.String("secret")
		if idx == 0 {
			return fmt.Errorf("--perm flag has to be one of: %s", AllPermissions)
		}

		// slice on [:idx] so for example: 'sign' gives you [read, write, sign]
		token, err := AuthNew(context.Background(), AllPermissions[:idx])
		if err != nil {
			return err
		}

		// TODO: Log in audit log when it is implemented

		fmt.Println(string(token))
		return nil

	},
}

type Config struct {
	Token     string
	APISecret string
}

var Cfg = Config{APISecret: "123456", Token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBbGxvdyI6WyJyZWFkIiwid3JpdGUiLCJzaWduIiwiYWRtaW4iXX0.ae22SYWjZ_RJRRhfWDpVFzWThu_6EQ-iBgAn8vdrR-w"}

var log = logging.Logger("rpc")

type jwtPayload struct {
	Allow []auth.Permission
}

const (
	// When changing these, update docs/API.md too

	PermRead  auth.Permission = "read" // default
	PermWrite auth.Permission = "write"
	PermSign  auth.Permission = "sign"  // Use wallet keys for signing
	PermAdmin auth.Permission = "admin" // Manage permissions
)

var AllPermissions = []auth.Permission{PermRead, PermWrite, PermSign, PermAdmin}
var DefaultPerms = []auth.Permission{}

func AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
	var payload jwtPayload
	if _, err := jwt.Verify([]byte(token), jwt.NewHS256([]byte(Cfg.APISecret)), &payload); err != nil {
		return nil, xerrors.Errorf("JWT Verification failed: %w", err)
	}

	return payload.Allow, nil
}

func AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) {
	p := jwtPayload{
		Allow: perms, // TODO: consider checking validity
	}

	return jwt.Sign(&p, jwt.NewHS256([]byte(Cfg.APISecret)))
}

func AuthHeader() http.Header {
	if len(Cfg.Token) != 0 {
		headers := http.Header{}
		headers.Add("Authorization", "Bearer "+Cfg.Token)
		return headers
	}
	log.Warn("API Token not set and requested, capabilities might be limited.")
	return nil
}
func AuthHeaderWtoken(t string) http.Header {
	headers := http.Header{}
	headers.Add("Authorization", "Bearer "+t)
	return headers
}

func GetRpcClient(t, url, name string, out interface{}) (func(), error) {
	closer, err := jsonrpc.NewClient(context.Background(), "http://"+url+"/"+name+"/v0", name, out, AuthHeaderWtoken(t))
	if err != nil {
		return nil, err
	}
	return closer, nil
}

func RegisterRpc(v, out interface{}, name string) *jsonrpc.RPCServer {

	rpcServer := jsonrpc.NewServer()

	// //c = &outChain
	auth.PermissionedProxy(AllPermissions, DefaultPerms, v, out)
	rpcServer.Register(name, out)
	//rpcServer.Register("asmb", acontext.Chain)

	return rpcServer
}
